/* -*- mode: C++; tab-width:4; c-basic-offset: 4; indent-tabs-mode:nil -*- */
/************************************************************************
 *
 *  File: MatTransforms.hpp
 *
 *  Created: 2009-08-13
 *
 *  Version:
 *
 *  Authors: Christopher Dyken <christopher.dyken@sintef.no>
 *
 *  This file is part of the siut library.
 *  Copyright (C) 2009 by SINTEF.  All rights reserved.
 *
 *  SINTEF, Pb 124 Blindern, N-0314 Oslo, Norway
 *  http://www.sintef.no
 *********************************************************************/

#ifndef _SIUT_SIMD_MATTRANSFORMS_HPP_H_
#define _SIUT_SIMD_MATTRANSFORMS_HPP_H_

#include "siut/simd/Mat4f.hpp"
#include "siut/simd/MatOperations.hpp"
#include "siut/simd/Quatf.hpp"

namespace siut {
    namespace simd {

inline
Mat4f
identityMatrixf()
{
    return matrixFromRowMajorEntriesf(   1.f, 0.f, 0.f, 0.f,
                                         0.f, 1.f, 0.f, 0.f,
                                         0.f, 0.f, 1.f, 0.f,
                                         0.f, 0.f, 0.f, 1.f );
}


/** Creates a perspective projection matrix, equivalent to gluPerspective.
 *
 * \param fovy    Field of view along y-axis, in radians.
 * \param aspect  Aspect ratio of view, x/y.
 * \param near    Distance to near-plane, must be greater than zero
 * \param far     Distance to far-plane, must be greater than near.
 * \return        The perspective transform matrix.
 */
inline
Mat4f
perspectiveMatrixf( float fovy, float aspect, float near, float far )
{
	float f = 1.0f/tanf( 0.5f*fovy );
	float g = f/aspect;
	float h = (far+near)/(near-far);
	float j = (2*far*near)/(near-far);

	return matrixFromRowMajorEntriesf(   g, 0.f,  0.f, 0.f,
				  0.f,   f,  0.f, 0.f,
                  0.f, 0.f,    h,   j,
                  0.f, 0.f, -1.f, 0.f );
}


/** Creates a perspective projection matrix, equivalent to glFrustum. */
inline
Mat4f
frustumMatrixf( float left, float right, float bottom, float top, float l_near, float l_far )
{
	float A = (right+left)/(right-left);
	float B = (top+bottom)/(top-bottom);
	float C = -(l_far+l_near)/(l_far-l_near);
	float D = (-2.0f*l_far*l_near)/(l_far-l_near);
	float E = (2.0f*l_near)/(right-left);
	float F = (2.0f*l_near)/(top-bottom);
	return matrixFromRowMajorEntriesf(   E, 0.f,    A, 0.f,
                                       0.f,   F,    B, 0.f,
                                       0.f, 0.f,    C,   D,
                                       0.f, 0.f, -1.f, 0.f );
}

/** Creates an inverse perspective projection matrix
 *
 * Creates the inverse of perspectiveMatrixf.
 *
 * \todo: currently finds the inverse numerically, but it should be
 * straightforward to do this analytically and insert formula.
 *
 * \param fovy    Field of view along y-axis, in radians.
 * \param aspect  Aspect ratio of view, x/y.
 * \param near    Distance to near-plane, must be greater than zero
 * \param far     Distance to far-plane, must be greater than near.
 * \return        The inverse perspective transform matrix.
 */
inline Mat4f
perspectiveMatrixInversef( float fovy, float aspect, float near, float far )
{
    Mat4f P = perspectiveMatrixf( fovy, aspect, near, far );
    return inverse_2( P );
}

/** Creates a translation matrix, equivalent to glTranslate.
 *
 * \param t  vector that specifies the translation.
 * \returns  The translation matrix.
 */
inline Mat4f
translationMatrixf( const Vec3f& t )
{
	return matrixFromRowMajorEntriesf( 1.f, 0.f, 0.f, t[0],
                  0.f, 1.f, 0.f, t[1],
                  0.f, 0.f, 1.f, t[2],
                  0.f, 0.f, 0.f, 1.0f );
}

/** Creates an inverse translation matrix.
 *
 * Creates the inverse of translationMatrixf.
 *
 * \param t  The vector that specifies the translation.
 * \returns  The inverse translation matrix.
 */
inline Mat4f
translationMatrixInversef( const Vec3f& t )
{
	return matrixFromRowMajorEntriesf( 1.f, 0.f, 0.f, -t[0],
                  0.f, 1.f, 0.f, -t[1],
                  0.f, 0.f, 1.f, -t[2],
                  0.f, 0.f, 0.f, 1.0f );
}

/** Creates a homogeneous rotation matrix.
 *
 * \param q  The rotation represented as a quaternion.
 * \returns  The rotation matrix.
 */
inline Mat4f 
rotationMatrix4f( const Quatf& q )
{
	float xx = q.q_[1]*q.q_[1]; float xy = q.q_[1]*q.q_[2]; float xz = q.q_[1]*q.q_[3];
    float xw = q.q_[1]*q.q_[0]; float yy = q.q_[2]*q.q_[2]; float yz = q.q_[2]*q.q_[3];
    float yw = q.q_[2]*q.q_[0]; float zz = q.q_[3]*q.q_[3]; float zw = q.q_[3]*q.q_[0];
		
    return
        matrixFromRowMajorEntriesf( 1.f-2.f*(yy+zz),     2.0f*(xy-zw),     2.0f*(xz+yw), 0.f,
                                        2.f*(xy+zw), 1.0f-2.0f*(xx+zz),     2.0f*(yz-xw), 0.f,
                                        2.f*(xz-yw),     2.0f*(yz+xw), 1.0f-2.0f*(xx+yy), 0.f,
                                                0.f,             0.f,             0.f, 1.f );
}

/** Creates an inverse homogeneous rotation matrix.
 *
 * \param q  The rotation represented as a quaternion.
 * \returns  The inverse rotation matrix.
 */
inline Mat4f 
rotationMatrixInverse4f( const Quatf& q )
{
    return rotationMatrix4f( conjugate(q) );
}

/** Creates a rotation matrix aligning view with a cube map face.
 *
 * \param   face 0..5 for faces GL_TEXTURE_CUBE_MAP_POSITIVE_X .. GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
 * \returns The rotation matrix.
 *
 */
inline Mat4f
cubeMapRotationMatrixf( int face )
{
    if( face == 0 ) {
        // GL_TEXTURE_CUBE_MAP_POSITIVE_X
        return matrixFromRowMajorEntriesf(  0.f, 0.f,-1.f, 0.f,
                                            0.f, 1.f, 0.f, 0.f,
                                            1.f, 0.f, 0.f, 0.f,
                                            0.f, 0.f, 0.f, 1.f );
    }
    else if( face == 1 ) {
        // GL_TEXTURE_CUBE_MAP_NEGATIVE_X
        return matrixFromRowMajorEntriesf( 0.f, 0.f, 1.f, 0.f,
                                           0.f, 1.f, 0.f, 0.f,
                                           -1.f, 0.f, 0.f, 0.f,
                                           0.f, 0.f, 0.f, 1.f );
    }
    else if( face == 2 ) {
        // GL_TEXTURE_CUBE_MAP_POSITIVE_Y
        return matrixFromRowMajorEntriesf( -1.f, 0.f, 0.f, 0.f,
                                           0.f, 0.f,-1.f, 0.f,
                                           0.f,-1.f, 0.f, 0.f,
                                           0.f, 0.f, 0.f, 1.f );
    }
    else if( face == 3 ) {
        // GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
        return matrixFromRowMajorEntriesf( -1.f, 0.f, 0.f, 0.f,
                                           0.f, 0.f, 1.f, 0.f,
                                           0.f, 1.f, 0.f, 0.f,
                                           0.f, 0.f, 0.f, 1.f );
    }
    else if( face == 4 ) {
        // GL_TEXTURE_CUBE_MAP_POSITIVE_Z
        return matrixFromRowMajorEntriesf( -1.f, 0.f, 0.f, 0.f,
                                           0.f, 1.f, 0.f, 0.f,
                                           0.f, 0.f,-1.f, 0.f,
                                           0.f, 0.f, 0.f, 1.f );
    }
    else {
        // GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
        return matrixFromRowMajorEntriesf( 1.f, 0.f, 0.f, 0.f,
                                           0.f, 1.f, 0.f, 0.f,
                                           0.f, 0.f, 1.f, 0.f,
                                           0.f, 0.f, 0.f, 1.f );
    }

}



    } // of namespace simd
} // of namepsace siut
#endif // _SIUT_SIMD_MATTRANSFORMS_HPP_H
